package com.gitee.wsl.transform.validator.sample

import kotlin.random.Random


/**
 * 统一社会信用代码（GB32100-2015）工具类<br></br>
 * 标准见：https://www.cods.org.cn/c/2020-10-29/12575.html
 *
 * <pre>
 * 第一部分：登记管理部门代码1位 (数字或大写英文字母)
 * 第二部分：机构类别代码1位 (数字或大写英文字母)
 * 第三部分：登记管理机关行政区划码6位 (数字)
 * 第四部分：主体标识码（组织机构代码）9位 (数字或大写英文字母)
 * 第五部分：校验码1位 (数字或大写英文字母)
</pre> *
 *
 * @author looly
 * @since 5.2.4
 */
object CreditCodeUtil {
    /**
     * 统一社会信用代码
     * <pre>
     * 第一部分：登记管理部门代码1位 (数字或大写英文字母)
     * 第二部分：机构类别代码1位 (数字或大写英文字母)
     * 第三部分：登记管理机关行政区划码6位 (数字)
     * 第四部分：主体标识码（组织机构代码）9位 (数字或大写英文字母)
     * 第五部分：校验码1位 (数字或大写英文字母)
    </pre> *
     */
    var CREDIT_CODE: String = "^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$"

    val CREDIT_CODE_PATTERN = CREDIT_CODE.toRegex()

    /**
     * 加权因子
     */
    private val WEIGHT = intArrayOf(1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28)

    /**
     * 代码字符集
     */
    private val BASE_CODE_ARRAY = "0123456789ABCDEFGHJKLMNPQRTUWXY".toCharArray()
    private val CODE_INDEX_MAP = BASE_CODE_ARRAY.mapIndexed { index, value -> value to index  }.toMap()

//    init {
//        CODE_INDEX_MAP = SafeConcurrentHashMap(BASE_CODE_ARRAY.size)
//        for (i in BASE_CODE_ARRAY.indices) {
//            CODE_INDEX_MAP.put(BASE_CODE_ARRAY[i], i)
//        }
//    }

    /**
     * 正则校验统一社会信用代码（18位）
     *
     * <pre>
     * 第一部分：登记管理部门代码1位 (数字或大写英文字母)
     * 第二部分：机构类别代码1位 (数字或大写英文字母)
     * 第三部分：登记管理机关行政区划码6位 (数字)
     * 第四部分：主体标识码（组织机构代码）9位 (数字或大写英文字母)
     * 第五部分：校验码1位 (数字或大写英文字母)
    </pre> *
     *
     * @param creditCode 统一社会信用代码
     * @return 校验结果
     */
    @OptIn(ExperimentalUnsignedTypes::class)
    fun isCreditCodeSimple(creditCode: CharSequence?): Boolean {
        if (creditCode.isNullOrBlank()) {
            return false
        }
        return CREDIT_CODE_PATTERN.matches( creditCode)
    }

    /**
     * 是否是有效的统一社会信用代码
     * <pre>
     * 第一部分：登记管理部门代码1位 (数字或大写英文字母)
     * 第二部分：机构类别代码1位 (数字或大写英文字母)
     * 第三部分：登记管理机关行政区划码6位 (数字)
     * 第四部分：主体标识码（组织机构代码）9位 (数字或大写英文字母)
     * 第五部分：校验码1位 (数字或大写英文字母)
    </pre> *
     *
     * @param creditCode 统一社会信用代码
     * @return 校验结果
     */
    fun isCreditCode(creditCode: CharSequence): Boolean {
        if (false == isCreditCodeSimple(creditCode)) {
            return false
        }

        val parityBit = getParityBit(creditCode)
        if (parityBit < 0) {
            return false
        }

        return creditCode.get(17) == BASE_CODE_ARRAY[parityBit]
    }

    /**
     * 获取一个随机的统一社会信用代码
     *
     * @return 统一社会信用代码
     */
    fun randomCreditCode(): String {
        val buf= StringBuilder(18)


        //
        for (i in 0..1) {
            val num: Int = Random.nextInt(BASE_CODE_ARRAY.size - 1)
            buf.append(BASE_CODE_ARRAY[num].uppercaseChar())
        }
        for (i in 2..7) {
            val num: Int = Random.nextInt(10)
            buf.append(BASE_CODE_ARRAY[num])
        }
        for (i in 8..16) {
            val num: Int = Random.nextInt(BASE_CODE_ARRAY.size - 1)
            buf.append(BASE_CODE_ARRAY[num])
        }

        val code = buf.toString()
        return code + BASE_CODE_ARRAY[getParityBit(code)]
    }

    /**
     * 获取校验位的值
     *
     * @param creditCode 统一社会信息代码
     * @return 获取校验位的值，-1表示获取错误
     */
    private fun getParityBit(creditCode: CharSequence): Int {
        var sum = 0
        var codeIndex: Int?
        for (i in 0..16) {
            codeIndex = CODE_INDEX_MAP[creditCode[i]]
            if (null == codeIndex) {
                return -1
            }
            sum += codeIndex * WEIGHT[i]
        }
        val result = 31 - sum % 31
        return if (result == 31) 0 else result
    }
}
